// Kony


#include "AbilitySystem/AbilityTasks/TargetDataUnderMouse.h"

#include "AbilitySystemComponent.h"
#include "GASC_HuanLing/GASC_HuanLing.h"

UTargetDataUnderMouse* UTargetDataUnderMouse::CreateTargetDataUnderMounse(UGameplayAbility* OwningAbility)
{
	//新建能力任务
	UTargetDataUnderMouse* MyObj = NewAbilityTask<UTargetDataUnderMouse>(OwningAbility);
	return MyObj;
}

void UTargetDataUnderMouse::Activate()
{
	//判断是否为本地控制
	const bool bIsLocallyControlled = Ability->GetCurrentActorInfo()->IsLocallyControlled();
	if (bIsLocallyControlled)
	{
		//如果是本地则发送目标数据，同时会发送给服务器
		SendMouseCursorData();
	}
	else
	{
		const FGameplayAbilitySpecHandle SpecHandle = GetAbilitySpecHandle();
		const FPredictionKey ActivationPredictionKey = GetActivationPredictionKey();
		//如果不是本地则注册一个回调函数，当目标数据被服务器确认后，会调用这个回调函数
		AbilitySystemComponent.Get()->AbilityTargetDataSetDelegate(SpecHandle, ActivationPredictionKey).AddUObject(this, &UTargetDataUnderMouse::OnTargetDataReplicatedCallback);

		//检查服务器是否已经确认了目标数据
		const bool bCalledDelegate = AbilitySystemComponent.Get()->CallReplicatedTargetDataDelegatesIfSet(SpecHandle, ActivationPredictionKey);
		if (!bCalledDelegate)
		{
			//如果失败则设置 继续等待玩家数据
			SetWaitingOnRemotePlayerData();
		}
	}
}

void UTargetDataUnderMouse::SendMouseCursorData()
{
	/*================== 开启一个预测作用域窗口 ==================
	 在客户端发送目标数据前，使用 FScopedPredictionWindow 来开启 GAS 的“预测窗口”，
	 使得以下操作（如生成 TargetData、发送 RPC）处于同一个预测作用域下。
	 这样在高延迟环境下，客户端也能立即看到技能释放的反馈效果（比如光标点中目标就能立刻响应）

	 ✅ 作用说明：
	 - 开启预测作用域（Prediction Key），标记接下来这段代码是“预测性的”
	 - 自动与 AbilitySystemComponent 的 ScopedPredictionKey 同步
	 - 在 PredictionKey 生命周期结束后自动失效
	============================================================*/
	FScopedPredictionWindow ScopedPrediction(AbilitySystemComponent.Get());
	//获取玩家控制器,用于从其中获取鼠标光标瞄准的目标
	APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
	//光标命中结果
	FHitResult CursorHit;
	//获取鼠标光标瞄准的目标 ECC_Visibility ECC_MouseTarget
	PC->GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);

	//用于封装发送给服务器的数据句柄
	FGameplayAbilityTargetDataHandle DataHandle;
	//GAS的游戏能力目标数据 单一目标命中数据 用于推送服务器
	FGameplayAbilityTargetData_SingleTargetHit* Data = new FGameplayAbilityTargetData_SingleTargetHit();
	//把光标的命中结果 赋值给GAS的目标数据
	Data->HitResult = CursorHit;
	DataHandle.Add(Data);
	//把目标数据通过能力系统发送给服务器
	/************** 发送目标数据到服务器（用于能力目标同步） **************
	 * 用于客户端将瞄准或选中的 TargetData 通过 RPC 发给服务器
	 * 
	 * 参数说明：
	 * - GetAbilitySpecHandle()         ：当前激活的能力句柄
	 * - GetActivationPredictionKey()   ：预测键，用于同步客户端预测和服务器验证
	 * - DataHandle                     ：封装好的目标数据（如命中的位置、Actor等）
	 * - FGameplayTag()                 ：可选的应用标签（这里传空）
	 * - ScopedPredictionKey            ：封装的预测作用域（自动管理同步状态）
	 ***********************************************************************/
	AbilitySystemComponent->ServerSetReplicatedTargetData(
		GetAbilitySpecHandle(),
		GetActivationPredictionKey(),
		DataHandle,
		FGameplayTag(),
		AbilitySystemComponent->ScopedPredictionKey);
	//这个适用于在能力未激活或者某些不应该广播的情况 禁止后续广播数据
	if (ShouldBroadcastAbilityTaskDelegates()){
		//广播目标位置
		ValidData.Broadcast(DataHandle);
	}
	
}

void UTargetDataUnderMouse::OnTargetDataReplicatedCallback(const FGameplayAbilityTargetDataHandle& DataHandle,FGameplayTag ActivationTag)
{
	//用于告诉客户端，目标数据已经确认接收了，不需要再缓存这些数据
	AbilitySystemComponent->ConsumeClientReplicatedTargetData(GetAbilitySpecHandle(), GetActivationPredictionKey());
	//这个适用于在能力未激活或者某些不应该广播的情况 禁止后续广播数据
	if (ShouldBroadcastAbilityTaskDelegates()){
		//广播目标位置
		ValidData.Broadcast(DataHandle);
	}
}
